#ifndef __LIBEVENT_SOCKET_WRAPPER_H__
#define __LIBEVENT_SOCKET_WRAPPER_H__

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdbool.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/thread.h>
#include <event2/bufferevent_ssl.h>
#include <event2/http.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>

#include <arpa/inet.h>
#include <netdb.h>
#include <sys/un.h>

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>

#include <stdbool.h>
#include <pthread.h>
#include <syslog.h>
#include <klist.h>

typedef int (*evsocket_callback)(struct bufferevent *, char *, unsigned int);

typedef int (*evsocket_event_callback)(struct bufferevent *, short, void *);

typedef enum role
{
    ROLE_MIN = 0,
    ROLE_SERVER,
    ROLE_CLIENT,
    ROLE_MAX
} role_te;

typedef enum evsocket_status
{
    /* 通用状态 */
    EVSOCKET_OK = 0,
    EVSOCKET_ERROR,

    /* Failures from socket API functions; most also save errno. */
    EVSOCKET_ERROR_GETADDRINFO,
    EVSOCKET_ERROR_SOCKET,
    EVSOCKET_ERROR_BIND,
    EVSOCKET_ERROR_LISTEN,
    EVSOCKET_ERROR_CONNECT,
    EVSOCKET_ERROR_FCNTL,

    /* Failure from snprintf: name too long. */
    EVSOCKET_ERROR_SNPRINTF,

    /* Invalid combination of options in configuration. */
    EVSOCKET_ERROR_CONFIGURATION,

    /* Error in setsockopt(2). */
    EVSOCKET_ERROR_SETSOCKOPT,

    EVSOCKET_ERROR_INVALID,
    EVSOCKET_ERROR_TYPE,
    EVSOCKET_ERROR_ROLE,
    EVSOCKET_ERROR_PATH,
    EVSOCKET_ERROR_CREAT,

    /*openSSL error*/
    EVSOCKET_ERROR_SSL_CTX_NEW,
    EVSOCKET_ERROR_SSL_NEW,
    EVSOCKET_ERROR_SSL_SETCERT,

    EVSOCKET_ERROR_NEW_BASE,
    EVSOCKET_ERROR_BUFFEREV_SOCKET_NEW,
    EVSOCKET_ERROR_HTTP_NEW,
    /* Other unknown error. */
    EVSOCKET_ERROR_UNKNOWN
} evsocket_status_te;

typedef enum family
{
    EV_FAMILY_MIN = 0,
    EV_FAMILY_UNIXDOMAIN,   /*unix域套接字*/
    EV_FAMILY_INET,         /*IPv4套接字*/
    EV_FAMILY_MAX
} family_te;

typedef enum evsocket_app_type
{
    EVSOCKET_TYPE_MIN = 0,
    EVSOCKET_TYPE_TCP,      /*未加密的TCP通信*/
    EVSOCKET_TYPE_TLS,      /*基于TLS的TCP通信*/
    EVSOCKET_TYPE_HTTP,     /*HTTP通信*/
    EVSOCKET_TYPE_HTTPS,    /*HTTPS通信*/
    EVSOCKET_TYPE_MAX
} evsocket_app_type_te;

typedef union address
{
    struct sockaddr_in sin;
    struct sockaddr_un sun;
} address_tu;

/* struct evsocket_config 结构中允许最大的套接字选项数.option_id为0表示选项数组的结束项*/
#define EVSOCKET_MAX_SOCK_OPTS 4
typedef struct evsocket_opt
{
    int option_id;
    void *value;
    socklen_t value_len;
} evsocket_opt_ts;

typedef struct accept_bufferevent_list
{
    int fd;
    struct bufferevent *bev;
    struct list_head list;
} accept_bufferevent_list_ts;

/* socket通信配置结构 */
typedef struct evsocket_config
{
    //libevent基本选项
    struct event_base *base;
    struct bufferevent *bev;
    unsigned int bevflags;              /*bufferevent的flags*/
    struct evconnlistener *listener;
    unsigned int levflags;              /*evconnlistener的flags*/

    //openssl项
    SSL *ssl;
    SSL_CTX *ssl_ctx;
    char *ca_crt;                       /*CA根证书*/
    char *server_crt;                   /*server证书文件*/
    char *private_key;                  /*私钥文件*/

    const char *host;
    family_te af;
    address_tu addr;
    unsigned short port;
    char *path;                 /* Path, for Unix domain socket. */
    role_te role;               /* server or client */
    int backlog;                /* set a custom backlog size */
    evsocket_app_type_te type;

    pthread_mutex_t acceptlist_mtx;
    //accept_bufferevent_list_ts acceptlist;
    struct list_head acceptlist;

    //libevent http项
    struct evhttp_uri *http_uri;
    struct evhttp_connection *evcon;
    struct evhttp_request *req;
    struct evkeyvalq *output_headers;
    struct evhttp *http;
    struct evhttp_bound_socket *handle;
    int retries;                /*重试次数*/
    int timeout;                /*超时*/
    //char *http_path;            /*URI path*/
    //char *http_query;           /*URI 请求*/
    void (*httpgencb)(struct evhttp_request *, void *);

    void (*httpcb)(struct evhttp_request *, void *);
    //void (*httpreqdonecb)(struct evhttp_request *, void *);

    char *delimiter;            /*TCP报文分隔符*/
    int delimiter_len;          /*分隔符长度*/

    evsocket_callback in;       /*读回调*/
    evsocket_callback out;      /*写回调*/
    evsocket_event_callback evcb;  /*事件回调*/
    evsocket_opt_ts sockopts[EVSOCKET_MAX_SOCK_OPTS];
} evsocket_config_ts;

/*状态结构*/
typedef struct evsocket_result
{
    int fd;
    evsocket_status_te status;
    int saved_errno;        /*保存的errno值*/
    /* Error code from getaddrinfo, only set if status is
     * SOCKET_ERROR_GETADDRINFO. See: gai_strerror(3). */
    int getaddrinfo_error;
} evsocket_result_ts;

typedef enum linkage_status
{
    EN_DISCONNECTED,
    EN_CONNECTED
} linkage_status_te;


int evsocket_create(evsocket_config_ts *cfg, evsocket_result_ts *res, struct event_base *attachbase);

int evsocket_release(evsocket_config_ts *cfg, evsocket_result_ts *res);

int evsocket_write(evsocket_config_ts *cfg, evsocket_result_ts *res, struct bufferevent *bevent, const char *buf,
                   int length);

int evsocket_http_uri_parse(evsocket_config_ts *cfg, evsocket_result_ts *out, const char *url);

int evsocket_http_add_header(evsocket_config_ts *cfg, evsocket_result_ts *res, const char *hfname, const char *hfvalue);

int evsocket_http_connection_setup(evsocket_config_ts *cfg, evsocket_result_ts *res);

int evsocket_http_request_new(evsocket_config_ts *cfg, evsocket_result_ts *res,
                              void (*httpreqdonecb)(struct evhttp_request *, void *));

int evsocket_http_make_request(evsocket_config_ts *cfg, evsocket_result_ts *res, enum evhttp_cmd_type type, char *uri);

int evsocket_http_setcb(evsocket_config_ts *cfg, evsocket_result_ts *res, const char *path,
                        void (*cb)(struct evhttp_request *, void *), void *cbargs);

int evsocket_http_setgencb(evsocket_config_ts *cfg, evsocket_result_ts *res,
                           void (*gencb)(struct evhttp_request *, void *), void *cbargs);

int evsocket_init_as_inet_server(evsocket_config_ts *config,
                                 bool withtls,
                                 char *const ca,
                                 char *const sca,
                                 char *const pk,
                                 char *const ip,
                                 unsigned short port,
                                 evsocket_callback r,
                                 evsocket_callback w,
                                 evsocket_event_callback evc,
                                 char *const delimter);

int evsocket_init_as_inet_client(evsocket_config_ts *config,
                                 bool withtls,
                                 char *const ca,
                                 char *const ip,
                                 unsigned short port,
                                 evsocket_callback r,
                                 evsocket_callback w,
                                 evsocket_event_callback evc,
                                 char *const delimter);

int evsocket_init_as_unix_domain_server(evsocket_config_ts *config,
                                        bool withtls,
                                        char *const ca,
                                        char *const sca,
                                        char *const pk,
                                        char *const path,
                                        evsocket_callback r,
                                        evsocket_callback w,
                                        evsocket_event_callback evc,
                                        char *const delimter);

int evsocket_init_as_unix_domain_client(evsocket_config_ts *config,
                                        bool withtls,
                                        char *const ca,
                                        char *const path,
                                        evsocket_callback r,
                                        evsocket_callback w,
                                        evsocket_event_callback evc,
                                        char *const delimter);

int evsocket_init_as_http_server(evsocket_config_ts *config,
                                 bool withtls,
                                 char *const ca,
                                 char *const sca,
                                 char *const pk,
                                 char *const ip,
                                 unsigned short port,
                                 void (*httpgencb)(struct evhttp_request *, void *));

int evsocket_init_as_http_client(evsocket_config_ts *config,
                                 bool withtls,
                                 char *const ca,
                                 char *const ip,
                                 unsigned short port/*,
                                          void (*httpreqdonecb)(struct evhttp_request *, void *)*/);

#endif
